﻿
Imports std
Imports storoomc

Partial Public MustInherit Class storoomOper(Of THREADMODEL As storoomThreadModel)
    'for those caches, which have no key defined
    Public Const cacheDefaultKey As Byte = 0
    Public enablecache As Boolean = False
    Private valuecache As cacheManager(Of String, bytes) = Nothing
    Private seekcache As cacheManager(Of String, Boolean) = Nothing
    Private capacitycache As cacheManager(Of Byte, UInt64) = Nothing
    Private emptycache As cacheManager(Of Byte, Boolean) = Nothing
    Private fullcache As cacheManager(Of Byte, Boolean) = Nothing
    Private valuesizecache As cacheManager(Of Byte, UInt64) = Nothing
    Private sizeofcache As cacheManager(Of String, UInt64) = Nothing
    Private keycountcache As cacheManager(Of Byte, UInt64) = Nothing

    Private cacheGetter As cacheGetterDef = Nothing
    Protected cacheUpdater As cacheUpdaterDef = Nothing

    Private Class cacheGetterDef
        Public caller As storoomOper(Of THREADMODEL) = Nothing

        Public Function value(ByVal key As String, ByRef buff As bytes) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.value.")
            Return caller.enablecache AndAlso caller.valuecache.get(key, buff)
        End Function

        Public Function seek(ByVal key As String, ByRef rtn As Boolean) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.seek.")
            Return caller.enablecache AndAlso caller.seekcache.get(key, rtn)
        End Function

        Public Function capacity(ByRef size As UInt64) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.capacity.")
            Return caller.enablecache AndAlso caller.capacitycache.get(cacheDefaultKey, size)
        End Function

        Public Function empty(ByRef rtn As Boolean) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.empty.")
            Return caller.enablecache AndAlso caller.emptycache.get(cacheDefaultKey, rtn)
        End Function

        Public Function full(ByRef rtn As Boolean) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.full.")
            Return caller.enablecache AndAlso caller.fullcache.get(cacheDefaultKey, rtn)
        End Function

        Public Function valuesize(ByRef sz As UInt64) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.valuesize.")
            Return caller.enablecache AndAlso caller.valuesizecache.get(cacheDefaultKey, sz)
        End Function

        Public Function sizeof(ByVal key As String, ByRef sz As UInt64) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.sizeof.")
            Return caller.enablecache AndAlso caller.sizeofcache.get(key, sz)
        End Function

        Public Function keycount(ByRef count As UInt64) As Boolean
            assert(Not caller Is Nothing, "caller is nothing before cacheGetter.keycount.")
            Return caller.enablecache AndAlso caller.keycountcache.get(cacheDefaultKey, count)
        End Function
    End Class

    'some cacheUpdater called in nodes_fixpoint
    Protected Class cacheUpdaterDef
        Public caller As storoomOper(Of THREADMODEL) = Nothing

        Private Sub addvaluecache(ByVal key As String, ByVal buff As bytes)
            'ignore to cache large value' 1M
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache AndAlso buff.size() <= (1 << 20) Then
                caller.valuecache.add(key, buff)
            End If
        End Sub

        Public Sub slicesCleaner()
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuesizecache.clear()
                caller.capacitycache.clear()
            End If
        End Sub

        Public Sub modify(ByVal key As String, ByVal buff As bytes, ByVal slice As storageInterface)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                addvaluecache(key, buff)
                caller.seekcache.add(key, True)
                caller.capacitycache.clear()
                caller.emptycache.add(cacheDefaultKey, False)
                caller.fullcache.clear()
                caller.valuesizecache.clear()
                caller.sizeofcache.add(key, buff.size)
                caller.keycountcache.clear()
            End If
        End Sub

        Public Sub write(ByVal key As String, ByVal buff As bytes)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuecache.erase(key)
                caller.seekcache.add(key, True)
                caller.capacitycache.clear()
                caller.emptycache.add(cacheDefaultKey, False)
                caller.fullcache.clear()
                caller.valuesizecache.clear()
                Dim size As Int64
                If caller.sizeofcache.get(key, size) Then
                    caller.sizeofcache.add(key, size + buff.size())
                End If
                caller.keycountcache.clear()
            End If
        End Sub

        Public Sub retire()
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuecache.clear()
                caller.seekcache.clear()
                caller.capacitycache.clear()
                caller.emptycache.add(cacheDefaultKey, True)
                caller.fullcache.add(cacheDefaultKey, False)
                caller.valuesizecache.clear()
                caller.sizeofcache.clear()
                caller.keycountcache.add(cacheDefaultKey, 0)
            End If
        End Sub

        Public Sub empty(ByVal rtn As Boolean)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.emptycache.add(cacheDefaultKey, rtn)
            End If
        End Sub

        Public Sub close()
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuecache.clear()
                caller.seekcache.clear()
                caller.capacitycache.clear()
                caller.emptycache.clear()
                caller.fullcache.clear()
                caller.valuesizecache.clear()
                caller.sizeofcache.clear()
                caller.keycountcache.clear()
            End If
        End Sub

        Public Sub open()
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.capacitycache.clear()
                caller.valuesizecache.clear()
                caller.keycountcache.clear()
            End If
        End Sub

        Public Sub seek(ByVal key As String, ByVal rtn As Boolean)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.seekcache.add(key, rtn)
            End If
        End Sub

        Public Sub read(ByVal key As String, ByVal buff As bytes)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                addvaluecache(key, buff)
                caller.sizeofcache.add(key, buff.size)
                caller.seekcache.add(key, True)
            End If
        End Sub

        Public Sub delete(ByVal key As String)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuecache.erase(key)
                caller.seekcache.add(key, False)
                caller.capacitycache.clear()
                caller.emptycache.clear()
                caller.fullcache.clear()
                caller.valuesizecache.clear()
                caller.sizeofcache.erase(key)
                'cannot +/- keycount
                'when write, it may increase the keycount, but in different threads, there may get a wrong version
                caller.keycountcache.clear()
            End If
        End Sub

        Public Sub full(ByVal rtn As Boolean)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.fullcache.add(cacheDefaultKey, rtn)
            End If
        End Sub

        Public Sub capacity(ByVal cap As UInt64)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.capacitycache.add(cacheDefaultKey, cap)
            End If
        End Sub

        Public Sub capacityClear()
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.capacitycache.clear()
            End If
        End Sub

        Public Sub valuesize(ByVal sz As UInt64)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.valuesizecache.add(cacheDefaultKey, sz)
            End If
        End Sub

        Public Sub sizeof(ByVal key As String, ByVal sz As UInt64)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.sizeofcache.add(key, sz)
            End If
        End Sub

        Public Sub keycount(ByVal count As UInt64)
            assert(Not caller Is Nothing, "caller is nothing before do cache cleaning.")
            If caller.enablecache Then
                caller.keycountcache.add(cacheDefaultKey, count)
            End If
        End Sub
    End Class

    Private Sub initialCache()
        If enablecache Then
            'do not retire the cache,
            'by design, if enable cache, should not change stornode / slice bypass this storoom / stornode
            valuecache = New cacheManager(Of String, bytes)(True, 128, npos)
            seekcache = New cacheManager(Of String, Boolean)(True, 1 << 18, npos)   '262144
            sizeofcache = New cacheManager(Of String, UInt64)(True, 1 << 18, npos)  '262144
            capacitycache = New cacheManager(Of Byte, UInt64)(True, 1, npos)
            emptycache = New cacheManager(Of Byte, Boolean)(True, 1, npos)
            fullcache = New cacheManager(Of Byte, Boolean)(True, 1, npos)
            valuesizecache = New cacheManager(Of Byte, UInt64)(True, 1, npos)
            keycountcache = New cacheManager(Of Byte, UInt64)(True, 1, npos)
        End If
        cacheGetter.caller = Me
        cacheUpdater.caller = Me
    End Sub
End Class
